package com.training.bt.controller;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.io.input.ThrottledInputStream;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import com.training.bt.common.PageResponseDTO;
import com.training.bt.config.DownloadLimitConfig;
import com.training.bt.entity.Resource;
import com.training.bt.entity.User;
import com.training.bt.entity.UserPoint;
import com.training.bt.resource.dto.ResourceDetailsDTO;
import com.training.bt.resource.dto.ResourceDownloadDTO;
import com.training.bt.resource.dto.ResourceListItemDTO;
import com.training.bt.resource.dto.ResourceMyDetailsDTO;
import com.training.bt.resource.dto.ResourceReviewDTO;
import com.training.bt.resource.dto.ResourceUploadDTO;
import com.training.bt.resource.service.ResourceService;
import com.training.bt.security.util.SecurityUtils;
import com.training.bt.service.DownloadHistoryService;
import com.training.bt.service.PromotionResourceService;
import com.training.bt.service.RecommendationService;
import com.training.bt.service.UserPointService;
import com.training.bt.user.service.UserExperienceService;
import com.training.bt.util.TorrentUtils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("api/resource")
@RequiredArgsConstructor
@Slf4j
public class ResourceController {
    private final ResourceService resourceService;
    private final UserPointService userPointService;
    private final DownloadHistoryService downloadHistoryService;
    private final RecommendationService recommendationService;
    private final PromotionResourceService promotionResourceService;
    private final UserExperienceService userExperienceService;

    @PostMapping("/upload")
    public ResponseEntity<?> upload(
            @RequestParam("title") String title,
            @RequestParam("description") String description,
            @RequestParam(value = "tags", required = false) String tagsCommaSeparated,
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "coverImage", required = false) MultipartFile coverImage) {

        // 转换逗号分隔的ID为List<Long>
        List<Long> tagIds = null;
        if (tagsCommaSeparated != null && !tagsCommaSeparated.isEmpty()) {
            tagIds = Arrays.stream(tagsCommaSeparated.split(","))
                    .map(String::trim)
                    .map(Long::parseLong)
                    .collect(Collectors.toList());
        }
        // 1. 校验文件是否为空
        if (file == null || file.isEmpty()) {
            return ResponseEntity
                    .badRequest()
                    .body("错误：上传文件不能为空");
        }

        // 2. 校验文件大小（示例：限制 10MB）
        long maxFileSize = 10L * 1024 * 1024;
        if (file.getSize() > maxFileSize) {
            return ResponseEntity
                    .badRequest()
                    .body("错误：文件大小不能超过 10MB");
        }

        // 3. 校验文件后缀名是否为 .torrent（优先） 或者校验 Content-Type
        String originalFilename = file.getOriginalFilename();
        if (originalFilename == null || !originalFilename.toLowerCase().endsWith(".torrent")) {
            return ResponseEntity
                    .badRequest()
                    .body("错误：仅支持 .torrent 文件");
        }

        try {
            // 4. 将前端参数封装成 DTO
            ResourceUploadDTO dto = new ResourceUploadDTO();
            dto.setTitle(title);
            dto.setDescription(description);
            dto.setFile(file);
            dto.setTagIds(tagIds);
            dto.setCoverImage(coverImage);

            // 5. 调用 Service 进行保存、注册到 Tracker，并返回刚插入的资源 ID
            Long resourceId = resourceService.uploadResource(dto);

            // 6. 组织返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("message", "上传成功");
            result.put("resourceId", resourceId);
            result.put("viewUrl", "/resource/" + resourceId);
            return ResponseEntity.ok(result);

        } catch (IOException e) {
            // 保存文件到磁盘或向 Tracker 注册时可能抛出 IOException
            log.error("文件上传 IO 异常: {}", e.getMessage(), e);
            return ResponseEntity
                    .status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("文件上传失败：服务器处理文件时发生错误");
        } catch (IllegalArgumentException e) {
            // 如果你的 Service 内部针对下载限制、标题长度等业务逻辑抛出了 IllegalArgumentException
            log.warn("上传参数错误: {}", e.getMessage());
            return ResponseEntity
                    .badRequest()
                    .body("参数错误：" + e.getMessage());
        } catch (Exception e) {
            // 捕获其他未知异常（兜底）
            log.error("上传接口发生未知异常: {}", e.getMessage(), e);
            return ResponseEntity
                    .status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body("服务器内部错误，请稍后重试");
        }
    }

    @GetMapping("/getList")
    public PageResponseDTO<ResourceListItemDTO> getResourceList(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(required = false) String keyword,
            @RequestParam(required = false) Long tagId) {

        return resourceService.getList(page, size, keyword, tagId);
    }

    @GetMapping("/{id}")
    public ResponseEntity<ResourceDetailsDTO> getResourceById(@PathVariable Long id) {
        return resourceService.findById(id);
    }

    @GetMapping("/my")
    public PageResponseDTO<ResourceMyDetailsDTO> getMyUploadList(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        return resourceService.getMyList(page, size);
    }

    @DeleteMapping("/{id}/delete")
    public ResponseEntity<?> delete(@PathVariable Long id) {
        resourceService.deleteById(id);
        return ResponseEntity.ok("删除成功");
    }

    @PostMapping("/payPoints/{id}")
    public ResponseEntity<?> payPoints(@PathVariable Long id) {
        Resource resource = resourceService.selectById(id);
        User user = SecurityUtils.getCurrentUser();
        UserPoint userPoint = userPointService.getUserPoint(user.getId());
        if (userPoint.getRatio().compareTo(BigDecimal.ONE) <= 0) {
            return ResponseEntity.badRequest().body("分享率过低，无法购买资源");
        }

        if (downloadHistoryService.exist(user.getId(), id) >= 1) {
            return ResponseEntity.badRequest().body("已购买，无需重复购买");
        }

        int points = resource.getPoints();
        resource.setDownloadCount(resource.getDownloadCount() + 1L);
        resourceService.updateDownloadCount(resource);

        userPointService.deductPoints(user.getId(), points, resource.getSize());

        downloadHistoryService.newDownloadHistory(id);

        return ResponseEntity.ok("积分购买成功");
    }

    @GetMapping("/download/{id}")
    public ResponseEntity<InputStreamResource> downloadTorrent(@PathVariable Long id) throws IOException {
        ResourceDownloadDTO dto = resourceService.getResourceFilePath(id);
        File file = new File(dto.getFilePath());

        User user = SecurityUtils.getCurrentUser();

        // 更改下载逻辑，此处应该为判断是否有购买记录，另创一个接口作为购买资源的接口。
        if (!downloadHistoryService.isPay(user.getId(), id)) {
            throw new RuntimeException("用户未购买资源");
        }

        String passkey = user.getPasskey();

        byte[] bytes = Files.readAllBytes(file.toPath());

        byte[] modifiedBytes = TorrentUtils.modifyAnnounce(bytes, passkey);

        int speedLimitKB = DownloadLimitConfig.DownloadLimitForRole(user.getRole());

        ThrottledInputStream.Builder builder = ThrottledInputStream.builder();
        ByteArrayInputStream byteInputStream = new ByteArrayInputStream(modifiedBytes);
        builder.setInputStream(byteInputStream);
        builder.setMaxBytesPerSecond(speedLimitKB);
        ThrottledInputStream throttledInputStream = builder.get();

        return ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION,
                        "attachment; filename=\"" + dto.getFilename() + "\"")
                .contentType(MediaType.parseMediaType("application/x-bittorrent"))
                .contentLength(modifiedBytes.length)
                .body(new InputStreamResource(throttledInputStream));
    }

    @PutMapping("/review")
    @PreAuthorize("hasRole('ADMIN')")
    public ResponseEntity<?> reviewResource(@RequestBody ResourceReviewDTO dto) {
        resourceService.reviewResource(dto);
        return ResponseEntity.ok("操作成功");
    }

    @GetMapping("/pending")
    @PreAuthorize("hasRole('ADMIN')")
    public PageResponseDTO<ResourceListItemDTO> getPendingResource(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        return resourceService.getPendingResource(page, size);
    }

    @GetMapping("/review/{id}")
    @PreAuthorize("hasRole('ADMIN')")
    public ResourceListItemDTO getReviewResource(@PathVariable Long id) {
        return resourceService.getReviewResource(id);
    }

    @GetMapping("/recommend")
    public PageResponseDTO<ResourceListItemDTO> recommend(@RequestParam int page, @RequestParam int size) {
        return recommendationService.calculateRecommendations(page, size);
    }
}
